//
//  File.swift
//  
//
//  Created by YuAo on 2020/3/16.
//

import Foundation
import ArgumentParser
import URLExpressibleByArgument
import RunCommand
import MetalPetalSourceLocator

public struct UmbrellaHeaderGenerator: ParsableCommand {
    
    @Argument(help: "The root directory of the MetalPetal repo.")
    var projectRoot: URL
    
    enum CodingKeys: CodingKey {
        case projectRoot
    }
    
    private let fileManager = FileManager()
    
    public init() { }
    
    public func run() throws {
        let directoryURL = MetalPetalSourcesRootURL(in: projectRoot)
        let privateHeaderFiles = try self.exludesHeaderFileNames()
        let headerFileNames = self.headerFileNames(in: directoryURL).filter({ !privateHeaderFiles.contains($0) })
        let content = """
        // MetalPetal Umbrella Header
        // Auto Generated by `swift run main boilerplate-generator`
        
        \(headerFileNames.reduce("", { result, value in return result + "#import \"\(value)\"\n"}))
        FOUNDATION_EXPORT double MetalPetalVersionNumber;
        FOUNDATION_EXPORT const unsigned char MetalPetalVersionString[];
        
        """
        try! content.write(to: directoryURL.appendingPathComponent("MetalPetal.h"), atomically: true, encoding: .utf8)
    }
    
    struct Podspec: Codable {
        var subspecs: [Podspec]?
        var name: String
        var private_header_files: [String]?
    }
    
    private func exludesHeaderFileNames() throws -> [String] {
        let podSpecFileURL = MetalPetalSourcesRootURL(in: projectRoot).appendingPathComponent("MetalPetal.podspec")
        let jsonString = try Command.execute("pod ipc spec \(podSpecFileURL.lastPathComponent)", fromDirectory: MetalPetalSourcesRootURL(in: projectRoot).path)
        let podspec = try JSONDecoder().decode(Podspec.self, from: jsonString.data(using: .utf8)!)
        var headers: [String] = []
        for subspec in podspec.subspecs ?? [] {
            headers.append(contentsOf: (subspec.private_header_files ?? []).map({ URL(fileURLWithPath: $0).lastPathComponent }))
        }
        let modulemap = MetalPetalSourcesRootURL(in: projectRoot).appendingPathComponent("MetalPetal.modulemap")
        let modulemapContents = try String(contentsOf: modulemap)
        modulemapContents.enumerateLines { (line, stop) in
            if line.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).hasPrefix("header") {
                let fields = line.split(separator: "\"")
                if fields.count >= 2 {
                    headers.append(String(fields[1]))
                }
            }
        }
        headers.append("MetalPetal.h")
        return headers
    }
    
    private func headerFileNames(in directory: URL) -> [String] {
        var fileNames: [String] = []
        if let contents = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: []) {
            for url in contents {
                if url.pathExtension == "h" {
                    fileNames.append(url.lastPathComponent)
                } else {
                    let isDirectory = try? url.resourceValues(forKeys: Set([URLResourceKey.isDirectoryKey])).isDirectory
                    if isDirectory ?? false {
                        fileNames.append(contentsOf: headerFileNames(in: url))
                    }
                }
            }
        }
        return fileNames
    }
}
